// 2-2 模板-类模板

/**
 * C++ 模板
 * 模板是**泛型编程**的基础，泛型编程即以一种独立于任何特定类型的方式编写代码。

 * 模板是创建泛型类或函数的蓝图或公式。库容器，比如**迭代器**和**算法**，都是泛型编程的例子，它们都使用了模板的概念。

 * 每个容器都有一个单一的定义，比如 向量，我们可以定义许多不同类型的向量，比如 vector <int> 或 vector <string>。

 * 您可以使用模板来定义**函数和类**，接下来让我们一起来看看如何使用。

 *-------------------------
 * 类模板
 * 正如我们定义函数模板一样，我们也可以定义类模板。泛型类声明的一般形式如下所示：

        template <class type> class class-name {
        .
        .
        .
        }
 * 在这里，type 是占位符类型名称，可以在类被实例化的时候进行指定。您可以**使用一个逗号分隔的列表**来定义多个泛型数据类型。

 * 下面的实例（实例一）定义了类 Stack<>，并实现了泛型方法来对元素进行入栈出栈操作：
*/
#include <iostream>
#define __mark 1
/**
 * #1 实例一
 * 栈 先进后出 （老电筒装电池）
 * 堆 先进先出 （高速路口,队列优先）
*/
#if 1 == __mark
#include <vector>
// template <calss T> // 两个都可以
template <typename T>
class Stack
{
private:
  std::vector<T> vec;

public:
  Stack();
  ~Stack();
  T top();              // 返回栈顶
  void push(T const &); // 入栈
  T pop();              // 出栈
  bool empty();         // 是否为空
};

template <class T>
Stack<T>::Stack()
{
  std::cout << "构造函数" << std::endl;
}

template <class T>
Stack<T>::~Stack()
{
  std::cout << "调用析构函数" << std::endl;
}

template <class T>
void Stack<T>::push(T const &obj)
{
  vec.push_back(obj);
}

template <class T>
T Stack<T>::pop()
{
  if (vec.empty())
  {
    throw std::out_of_range("Stack<>::pop : empty stack");
  }
  T temp = vec.back(); // 返回最后一个元素的副本
  vec.pop_back();      // 删除最后一个元素
  return temp;
}

template <class T>
bool Stack<T>::empty()
{
  return vec.empty();
}

template <class T>
T Stack<T>::top()
{
  if (vec.empty())
  {
    throw std::out_of_range("Stack<>top : Stack empty!");
  }
  return vec.back(); // 返回最后一个元素的副本
}

int main(void)
{
  try
  {
    Stack<int> intStack; // 整数栈
    intStack.push(1);
    intStack.push(2);
    intStack.push(3);
    std::cout << "top of intStack : " << intStack.top() << std::endl;

    intStack.pop();
    std::cout << "top of intStack : " << intStack.top() << std::endl;
    intStack.pop();
    intStack.pop();
    // intStack.pop();

    Stack<std::string> strStack; // 字符串栈
    strStack.push("u");
    strStack.push("love");
    strStack.push("I");
    // strStack.pop();
    // strStack.pop();
    // strStack.pop();
    
    std::cout << strStack.pop() << std::endl;
    std::cout << strStack.pop() << std::endl;
    std::cout << strStack.pop() << std::endl;
  }
  catch (std::out_of_range &ex)
  {
    std::cout << "out_of_range : " << ex.what() << std::endl;
  }
  catch (std::exception &ex)
  {
    std::cout << "exception : " << ex.what() << std::endl;
  }
  catch (...)
  {
    std::cout << "error " << std::endl;
  }
  printf("-----------------end------------------\n");
  return 0;
}

/**
 * #2 
*/
#elif 2 == __mark

int main(void)
{
  printf("-----------------end------------------\n");
  return 0;
}
#endif